﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Xml;
using EnvDTE;
using EnvDTE80;

namespace BaseAddIn.Rules
{
    public abstract class BaseRuleAddIn : IDisposable
    {
        #region Fields

        private XmlNode _ruleNode = null;
        private Dictionary<string, ArquivoExaminado> _arquivosExaminados;
        private ArquivoExaminado _current = null;
        private List<ProblemAddIn> _listaProblemas;

        #endregion

        #region Construtor

        public BaseRuleAddIn(string name, string resourceName, Assembly resourceAssembly)
        {
            Stream resourceXml = resourceAssembly.GetManifestResourceStream(String.Concat(resourceName, ".xml"));
            XmlDocument doc = new XmlDocument();
            doc.Load(resourceXml);

            foreach (XmlNode node in doc["Rules"])
            {
                if (node.Name == "Rule"
                    && node.Attributes["TypeName"] != null
                    && node.Attributes["TypeName"].Value == name)
                {
                    _ruleNode = node;
                    break;
                }
            }

            resourceXml.Close();
            resourceXml.Dispose();

            _arquivosExaminados = new Dictionary<string, ArquivoExaminado>();
        }

        #endregion

        #region Public Methods

        public void ResetaStatusAnalise()
        {
            foreach (string key in _arquivosExaminados.Keys)
            {
                _arquivosExaminados[key].RestartaAnalise();
            }

            _current = null;
        }

        public void Check(Project projeto, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(projeto);
        }

        public void Check(ProjectItem item, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(item);
        }

        public void Check(CodeElement cpdeElement, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(cpdeElement);
        }

        public void Check(CodeNamespace namespaceElement, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(namespaceElement);
        }

        public void Check(CodeInterface2 inter, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(inter);
        }

        public void Check(CodeClass2 classe, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(classe);
        }

        public void Check(CodeFunction funcao, CodeNamespace namespaceElement, CodeClass2 classe, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(funcao, namespaceElement, classe);
        }

        public void Check(CodeVariable variavel, CodeNamespace namespaceElement, CodeClass2 classe, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(variavel, namespaceElement, classe);
        }

        public void Check(CodeProperty propriedade, CodeNamespace namespaceElement, CodeClass2 classe, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(propriedade, namespaceElement, classe);
        }

        public void Check(CodeEnum enumElement, CodeNamespace namespaceElement, CodeClass2 classe, string filePath)
        {
            PreparaRule(filePath);

            if (!_current.AnaliseParada)
                Check(enumElement, namespaceElement, classe);
        }

        public void IniciaAnalise()
        {
            Before_IniciaAnalise();

            if (_listaProblemas != null)
                _listaProblemas.Clear();
            else
                _listaProblemas = new List<ProblemAddIn>();
        }

        public List<ProblemAddIn> FinalizaAnalise()
        {
            Before_FinalizaAnalise();

            return _listaProblemas;
        }

        #endregion

        #region Public Virtual Methods

        public virtual void Dispose()
        {
        }

        #endregion

        #region Protected Virtual Methods

        protected virtual void Before_IniciaAnalise()
        {
        }

        protected virtual void Before_FinalizaAnalise()
        {
        }

        protected virtual void Check(Project projeto)
        {
        }

        protected virtual void Check(ProjectItem item)
        {
        }

        protected virtual void Check(CodeElement codeElement)
        {
        }

        protected virtual void Check(CodeNamespace namespaceElement)
        {
        }

        protected virtual void Check(CodeInterface2 inter)
        {
        }

        protected virtual void Check(CodeClass2 classe)
        {
        }

        protected virtual void Check(CodeFunction funcao, CodeNamespace namespaceElement, CodeClass2 classe)
        {
        }

        protected virtual void Check(CodeVariable variavel, CodeNamespace namespaceElement, CodeClass2 classe)
        {
        }

        protected virtual void Check(CodeProperty propriedade, CodeNamespace namespaceElement, CodeClass2 classe)
        {
        }

        protected virtual void Check(CodeEnum enumElement, CodeNamespace namespaceElement, CodeClass2 classe)
        {
        }

        #endregion

        #region Protected Methods

        protected void GeraProblemaAddIn(string document,
                                                 string resolutionName,
                                                 params string[] resolutionArguments)
        {
            _listaProblemas.Add(new ProblemAddIn(GetNamedResolution(resolutionName,
                                                                    resolutionArguments),
                                                 Microsoft.VisualStudio.Shell.TaskPriority.High,
                                                 document,
                                                 0,
                                                 0));

            ParaAnalise();
        }

        protected void GeraProblemaAddIn(string document,
                                                 int line,
                                                 int column,
                                                 string resolutionName,
                                                 params string[] resolutionArguments)
        {
            _listaProblemas.Add(new ProblemAddIn(GetNamedResolution(resolutionName,
                                                                    resolutionArguments),
                                                 Microsoft.VisualStudio.Shell.TaskPriority.High,
                                                 document,
                                                 line - 1,
                                                 column - 1));

            ParaAnalise();
        }

        #endregion

        #region Private Methods

        private void ParaAnalise()
        {
            if (_current != null)
                _current.ParaAnalise();
        }

        private void PreparaRule(string filePath)
        {
            if (!this._arquivosExaminados.ContainsKey(filePath))
            {
                this._arquivosExaminados.Add(filePath,
                                             new ArquivoExaminado(filePath));
            }

            _current = this._arquivosExaminados[filePath];
        }

        private string GetNamedResolution(string resolutionName, params string[] resolutionArguments)
        {
            string retorno = string.Empty;

            foreach (XmlNode node in _ruleNode)
            {
                if (node.Name == "Resolution"
                    && node.Attributes["Name"] != null
                    && node.Attributes["Name"].Value == resolutionName)
                {
                    try
                    {
                        retorno = String.Format(node.InnerText, resolutionArguments);
                    }
                    catch
                    {
                        retorno = node.InnerText;
                    }

                    break;
                }
            }

            return retorno;
        }

        #endregion
    }
}
